package utils.binary.impl;

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import utils.binary.ByteSequence;
import utils.binary.BytesSerializable;
import utils.binary.BytesUtils;

import java.io.Serializable;

/**
 * <h1>Bytes 数据类型</h1>
 * <p>Bytes 被设计为不可变对象</p>
 *
 * @author Zhou Yifan
 */
public class Bytes implements ByteSequence, BytesSerializable, Serializable {

    private static final long serialVersionUID = 4774903322403127601L;

    public static final int INIT_CODE = 1;

    private static final Bytes EMPTY = new Bytes();

    private static final int MAX_CACHE = 256;

    /**
     * int 类型字节数组
     */
    private static final Bytes[] INT_BYTES;

    /**
     * long 类型字节数组
     */
    private static final Bytes[] LONG_BYTES;

    static {
        INT_BYTES = new Bytes[MAX_CACHE];
        LONG_BYTES = new Bytes[MAX_CACHE];
        for (int i = 0; i < MAX_CACHE; i++) {
            INT_BYTES[i] = new Bytes();
            LONG_BYTES[i] = new Bytes();
        }
    }

    /**
     * 前缀
     */
    private final Bytes prefix;

    /**
     * 前缀大小
     */
    private final int prefixSize;

    private final byte[] bytes;

    private final int hashCode;

    /* 构造函数部分 */

    public Bytes() {
        prefix = null;
        prefixSize = 0;
        bytes = BytesUtils.EMPTY_BYTES;
        hashCode = hashCode(INIT_CODE);
    }

    public Bytes(byte[] bytes) {
        if (bytes == null) {
            throw new IllegalArgumentException("The bytes data is null!");
        }
        this.prefix = null;
        this.prefixSize = 0;
        this.bytes = bytes;
        hashCode = hashCode(INIT_CODE);
    }

    public Bytes(Bytes prefix, byte[] bytes) {
        if (prefix == null) {
            throw new IllegalArgumentException("Prefix is null!");
        }
        if (bytes == null) {
            throw new IllegalArgumentException("The bytes data is null!");
        }
        this.prefix = prefix;
        this.prefixSize = prefix.size();
        this.bytes = bytes;
        hashCode = hashCode(INIT_CODE);
    }

    public Bytes(Bytes prefix, Bytes bytes) {
        if (prefix == null) {
            throw new IllegalArgumentException("Prefix is null!");
        }
        if (bytes == null) {
            throw new IllegalArgumentException("The bytes data is null!");
        }
        this.prefix = prefix;
        this.prefixSize = prefix.size();
        this.bytes = bytes.toBytes();

        hashCode = hashCode(INIT_CODE);
    }

    /* 接口实现部分 */

    /**
     * 字节序列的大小
     *
     * @return int 值
     */
    @Override
    public int size() {
        return prefixSize + bytes.length;
    }

    /**
     * 获取指定位置的字节
     *
     * @param index 指定位置，int 值
     * @return 字节， byte 值
     */
    @Override
    public byte byteAt(int index) {
        return read(index);
    }

    /**
     * 获取字节序列的子序列
     *
     * @param start 开始位置，int 值，包含
     * @param end   结束位置，int 值，不包含
     * @return 字节序列 {@link ByteSequence}
     */
    @Override
    public ByteSequence subSequence(int start, int end) {
        if (start < 0) {
            throw new IndexOutOfBoundsException(
                    "Start index of subsequence is out of bounds! --[start="
                            + start + "]"
            );
        }
        int s = end - start;
        if (s < 0 || s > size()) {
            throw new IndexOutOfBoundsException(
                    "End index of subsequence is out of bounds! --[start="
                            + start + ", end=" + end + "]"
            );
        }
        return new SubSequence(this, start, s);
    }

    /**
     * 从指定位置开始，将当前字节序列复制到目标字节数组的指定位置
     *
     * @param srcOffset    当前字节数组开始复制的位置
     * @param target       目标字节序列
     * @param targetOffset 目标字节数组开始位置
     * @param length       复制长度
     * @return int 值，表示复制的字节长度
     */
    @Override
    public int copyTo(int srcOffset, byte[] target, int targetOffset, int length) {
        if (srcOffset < 0) {
            throw new IllegalArgumentException(
                    "SourceOffset is negative! --[srcOffset="
                            + srcOffset + "]"
            );
        }
        if (targetOffset < 0) {
            throw new IllegalArgumentException(
                    "TargetOffset is negative! --[targetOffset="
                            + targetOffset + "]"
            );
        }
        if (length < 0) {
            throw new IllegalArgumentException(
                    "Length is negative! --[length="
                            + length + "]"
            );
        }
        if (length == 0) {
            return 0;
        }
        int s = 0;
        if (prefix != null && srcOffset < prefix.size()) {
            s = prefix.copyTo(target, targetOffset, length);
        }
        if (s < length) {
            int l = length - s;
            l = Math.min(l, bytes.length);
            System.arraycopy(bytes, 0, target, targetOffset + s, l);
            s += l;
        }
        return s;
    }

    /**
     * 以字节数组形式获取字节块的副本
     *
     * @return byte[]
     */
    @Override
    public byte[] toBytes() {
        if (prefix == null || prefix.size() == 0) {
            return bytes.clone();
        }
        int size = size();
        byte[] buffer = new byte[size];
        copyTo(buffer, 0, size);
        return buffer;
    }


    /* 工具函数部分 */

    /**
     * 根据初始代码计算 {@link #bytes} 的哈希值
     *
     * @param initCode 初始代码
     * @return int 哈希值
     */
    private int hashCode(int initCode) {
        if (prefix != null) {
            initCode = prefix.hashCode(initCode);
        }
        return hashCode(initCode, bytes);
    }

    /**
     * <p>根据初始化代码和字节数组计算其哈希值</p>
     * <p>哈希值的计算方式为：H(n + 1) = 31 * H(n) + bytes[n + 1]</p>
     * <p><b>关于哈希值计算的两点说明：</b></p>
     * <ul>
     *     <li>乘以 31 是因为 31 的二进制全是 1，这样方便离散数据</li>
     *     <li></li>
     * </ul>
     *
     * @param initCode 初始化代码
     * @param bytes    字节数组
     * @return int 哈希值
     */
    @Contract(pure = true)
    private int hashCode(int initCode, byte @NotNull [] bytes) {
        for (byte element : bytes) {
            initCode = 31 * initCode + element;
        }
        return initCode;
    }

    /**
     * 读取 Bytes 中指定索引位置的内容
     *
     * @param index 指定索引
     * @return byte 值
     */
    public byte read(int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException(
                    "Index is negative! --[index="
                            + index + "]"
            );
        }
        // 如果索引落在了前缀中，就在前缀中找
        if (index < prefixSize) {
            return prefix.read(index);
        }
        // 如果索引落在了数据中，就在数据中找就可以了，不要忘记减去前缀长度
        if (index < (prefixSize + bytes.length)) {
            return bytes[index - prefixSize];
        }
        throw new IndexOutOfBoundsException(
                "Index out of bound! --[index="
                        + index + "]"
        );
    }

    /**
     * 将 {@link #bytes} 赋值到指定数组中
     *
     * @param buffer 目标字节序列
     * @param offset 目标字节数组开始位置
     * @param length 复制长度
     * @return 复制长度
     */
    @Override
    public int copyTo(byte[] buffer, int offset, int length) {
        if (length < 0) {
            throw new IllegalArgumentException(
                    "Length is negative! --[index="
                            + length + "]"
            );
        }
        if (length == 0) {
            return 0;
        }
        int s = 0;
        if (prefix != null) {
            s = prefix.copyTo(buffer, offset, length);
        }
        if (s < length) {
            int l = length - s;
            l = Math.min(l, bytes.length);
            System.arraycopy(bytes, 0, buffer, offset + s, l);
            s += l;
        }
        return s;
    }

    /**
     * 以默认字符集 {@link BytesUtils#DEFAULT_CHARSET} 把字符串转为字节形式
     *
     * @param str 待转字符串
     * @return {@link Bytes}
     */
    @Contract("_ -> new")
    public static @NotNull Bytes fromString(String str) {
        return new Bytes(BytesUtils.toBytes(str));
    }

    public static Bytes fromInt(int value) {
        if (value > -1 && value < MAX_CACHE) {
            return INT_BYTES[value];
        }
        return new Bytes(BytesUtils.toBytes(value));
    }

    public static Bytes fromLong(long value) {
        if (value > -1 && value < MAX_CACHE) {
            return LONG_BYTES[(int) value];
        }
        return new Bytes(BytesUtils.toBytes(value));
    }

    /**
     * 子序列，内部类
     */
    public static class SubSequence implements ByteSequence {

        /**
         * 序列
         */
        private ByteSequence bytes;

        /**
         * 偏移位置
         */
        private int offset;

        /**
         * 序列大小
         */
        private int size;

        public SubSequence(ByteSequence bytes, int offset, int size) {
            this.offset = offset;
            this.size = size;
            this.bytes = bytes;
        }

        /**
         * 字节序列的大小
         *
         * @return int 值
         */
        @Override
        public int size() {
            return size;
        }

        /**
         * 获取指定位置的字节
         *
         * @param index 指定位置，int 值
         * @return 字节， byte 值
         */
        @Override
        public byte byteAt(int index) {
            if (index < 0 || index >= size) {
                throw new IndexOutOfBoundsException(
                        "Index of subsequence is out of bounds! --[index="
                                + index + "]"
                );
            }
            return bytes.byteAt(offset + index);
        }

        /**
         * 获取字节序列的子序列
         *
         * @param start 开始位置，int 值，包含
         * @param end   结束位置，int 值，不包含
         * @return 字节序列 {@link ByteSequence}
         */
        @Override
        public ByteSequence subSequence(int start, int end) {
            if (start < 0) {
                throw new IndexOutOfBoundsException(
                        "Start index of subsequence is out of bounds! --[start="
                                + start + "]"
                );
            }
            int s = end - start;
            if (s < 0 || s > size) {
                throw new IndexOutOfBoundsException(
                        "End index of subsequence is out of bounds! --[start="
                                + start + ", end=" + end + "]"
                );
            }
            return new SubSequence(bytes, offset + start, s);
        }

        /**
         * 从指定位置开始，将当前字节序列复制到目标字节数组的指定位置
         *
         * @param srcOffset    当前字节数组开始复制的位置
         * @param target       目标字节序列
         * @param targetOffset 目标字节数组开始位置
         * @param length       复制长度
         * @return int 值，表示复制的字节长度
         */
        @Override
        public int copyTo(int srcOffset, byte[] target, int targetOffset, int length) {
            return bytes.copyTo(offset + srcOffset, target, targetOffset, length);
        }
    }
}
